package com.book.common.util;

import com.google.common.collect.Lists;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Description：获取客户端IP信息
 *
 * @Author： leo.xiong
 * @CreateDate： 2019/1/8 15:16
 * @Email： leo.xiong@suyun360.com
 * Version：1.0
 */
public final class RequestInfoUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestInfoUtil.class);
    public static final String COMMA = ",";

    public static final String COLON = ":";

    public static String IP = "";

    private static final String UNKNOWN = "unknown";

    private static final int MAX_IP_LENGTH = 15;

    private static final int MIN_IP_LENGTH = 7;

    public static final String LOCAL_HOST = "127.0.0.1";

    public static final List<String> IP_LIST = Lists.newArrayList();

    /**
     * 获取服务器IP地址
     */
    static {
        try {
            Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
            while (netInterfaces.hasMoreElements()) {
                Enumeration<InetAddress> ni = ((NetworkInterface) netInterfaces.nextElement()).getInetAddresses();
                String ip;
                if (ni.hasMoreElements()) {
                    ip = ni.nextElement().getHostAddress();
                    if (StringUtils.isEmpty(ip)) {
                        continue;
                    }
                    if (!LOCAL_HOST.equals(ip) && ip.indexOf(COLON) == -1 && isIP(ip)) {
                        if (StringUtils.isEmpty(IP)) {
                            IP = ip;
                            LOGGER.info("当前机器受用的IP是" + IP);
                        }
                        IP_LIST.add(ip);
                    }
                }
            }
            LOGGER.info("所有的IP信息:{}", IP_LIST);
        } catch (SocketException e) {
            LOGGER.error("获取当前服务器IP失败" + e);
        }
    }

    public static boolean isIP(String addr) {
        if (addr.length() < MIN_IP_LENGTH || addr.length() > MAX_IP_LENGTH || "".equals(addr)) {
            return false;
        }
        /**
         * 判断IP格式和范围
         */
        String rexp = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
        Pattern pat = Pattern.compile(rexp);
        Matcher mat = pat.matcher(addr);
        boolean ipAddress = mat.find();
        return ipAddress;
    }

    /**
     * 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址;
     *
     * @param request
     * @return
     */
    public final static String getIpAddress(HttpServletRequest request) {
        // 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址
        String ip = request.getHeader("X-Forwarded-For");
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("getIpAddress(HttpServletRequest) - X-Forwarded-For - String ip=" + ip);
        }
        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("getIpAddress(HttpServletRequest) - Proxy-Client-IP - String ip=" + ip);
                }
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("getIpAddress(HttpServletRequest) - WL-Proxy-Client-IP - String ip=" + ip);
                }
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("getIpAddress(HttpServletRequest) - HTTP_CLIENT_IP - String ip=" + ip);
                }
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("getIpAddress(HttpServletRequest) - HTTP_X_FORWARDED_FOR - String ip=" + ip);
                }
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("getIpAddress(HttpServletRequest) - getRemoteAddr - String ip=" + ip);
                }
            }
        } else if (ip.length() > MAX_IP_LENGTH) {
            String[] ips = ip.split(COMMA);
            for (int index = 0; index < ips.length; index++) {
                String strIp = ips[index];
                if (!(UNKNOWN.equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
        return ip;
    }

    /**
     * 获取浏览器类型和版本
     *
     * @param request
     * @return
     */
    public static String getBrowser(HttpServletRequest request) {
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        return (userAgent.getBrowser().toString() + userAgent.getBrowserVersion().toString());
    }

    /**
     * 获取系统类型
     *
     * @param request
     * @return
     */
    public static String getSystem(HttpServletRequest request) {
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        return userAgent.getOperatingSystem().toString();
    }

    /**
     * 是否是本地地址
     *
     * @param ip
     * @return
     */
    public static boolean isLocalAddr(String ip) {
        if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
            return true;
        } else {
            return false;
        }
    }

    public static String getIp(InetSocketAddress inetSocketAddress) {
        String hostName = inetSocketAddress.getHostName();
        return hostName;
    }

    /**
     * 判断IP地址为内网IP还是公网IP
     * tcp/ip协议中，专门保留了三个IP地址区域作为私有地址，其地址范围如下：
     * 10.0.0.0/8：10.0.0.0～10.255.255.255
     * 172.16.0.0/12：172.16.0.0～172.31.255.255
     * 192.168.0.0/16：192.168.0.0～192.168.255.255
     *
     * @param ip
     * @return
     */
    public static boolean isInternalAddr(String ip) {
        if (isLocalAddr(ip)) {
            return true;
        }
        byte[] addr = textToNumericFormatV4(ip);

        final byte b0 = addr[0];
        final byte b1 = addr[1];
        //10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        //172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        //192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                switch (b1) {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }

    public static byte[] textToNumericFormatV4(String paramString) {
        if (paramString.length() == 0) {
            return null;
        }
        byte[] arrayOfByte = new byte[4];
        String[] arrayOfString = paramString.split("\\.", -1);
        try {
            long l;
            int i;
            switch (arrayOfString.length) {
                case 1:
                    l = Long.parseLong(arrayOfString[0]);
                    if ((l < 0L) || (l > 4294967295L)) {
                        return null;
                    }
                    arrayOfByte[0] = ((byte) (int) (l >> 24 & 0xFF));
                    arrayOfByte[1] = ((byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF));
                    arrayOfByte[2] = ((byte) (int) ((l & 0xFFFF) >> 8 & 0xFF));
                    arrayOfByte[3] = ((byte) (int) (l & 0xFF));
                    break;
                case 2:
                    l = Integer.parseInt(arrayOfString[0]);
                    if ((l < 0L) || (l > 255L)) {
                        return null;
                    }
                    arrayOfByte[0] = ((byte) (int) (l & 0xFF));
                    l = Integer.parseInt(arrayOfString[1]);
                    if ((l < 0L) || (l > 16777215L)) {
                        return null;
                    }
                    arrayOfByte[1] = ((byte) (int) (l >> 16 & 0xFF));
                    arrayOfByte[2] = ((byte) (int) ((l & 0xFFFF) >> 8 & 0xFF));
                    arrayOfByte[3] = ((byte) (int) (l & 0xFF));
                    break;
                case 3:
                    for (i = 0; i < 2; i++) {
                        l = Integer.parseInt(arrayOfString[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        arrayOfByte[i] = ((byte) (int) (l & 0xFF));
                    }
                    l = Integer.parseInt(arrayOfString[2]);
                    if ((l < 0L) || (l > 65535L)) {
                        return null;
                    }
                    arrayOfByte[2] = ((byte) (int) (l >> 8 & 0xFF));
                    arrayOfByte[3] = ((byte) (int) (l & 0xFF));
                    break;
                case 4:
                    for (i = 0; i < 4; i++) {
                        l = Integer.parseInt(arrayOfString[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        arrayOfByte[i] = ((byte) (int) (l & 0xFF));
                    }
                    break;
                default:
                    return null;
            }
        } catch (NumberFormatException localNumberFormatException) {
            return null;
        }
        return arrayOfByte;
    }

    public static byte[] textToNumericFormatV6(String paramString) {
        if (paramString.length() < 2) {
            return null;
        }
        char[] arrayOfChar = paramString.toCharArray();
        byte[] arrayOfByte1 = new byte[16];

        int m = arrayOfChar.length;
        int n = paramString.indexOf("%");
        if (n == m - 1) {
            return null;
        }
        if (n != -1) {
            m = n;
        }
        int i = -1;
        int i1 = 0;
        int i2 = 0;
        if ((arrayOfChar[i1] == ':') && (arrayOfChar[(++i1)] != ':')) {
            return null;
        }
        int i3 = i1;
        int j = 0;
        int k = 0;
        int i4;
        while (i1 < m) {
            char c = arrayOfChar[(i1++)];
            i4 = Character.digit(c, 16);
            if (i4 != -1) {
                k <<= 4;
                k |= i4;
                if (k > 65535) {
                    return null;
                }
                j = 1;
            } else if (c == ':') {
                i3 = i1;
                if (j == 0) {
                    if (i != -1) {
                        return null;
                    }
                    i = i2;
                } else {
                    if (i1 == m) {
                        return null;
                    }
                    if (i2 + 2 > 16) {
                        return null;
                    }
                    arrayOfByte1[(i2++)] = ((byte) (k >> 8 & 0xFF));
                    arrayOfByte1[(i2++)] = ((byte) (k & 0xFF));
                    j = 0;
                    k = 0;
                }
            } else if ((c == '.') && (i2 + 4 <= 16)) {
                String str = paramString.substring(i3, m);

                int i5 = 0;
                int i6 = 0;
                while ((i6 = str.indexOf('.', i6)) != -1) {
                    i5++;
                    i6++;
                }
                if (i5 != 3) {
                    return null;
                }
                byte[] arrayOfByte3 = textToNumericFormatV4(str);
                if (arrayOfByte3 == null) {
                    return null;
                }
                for (int i7 = 0; i7 < 4; i7++) {
                    arrayOfByte1[(i2++)] = arrayOfByte3[i7];
                }
                j = 0;
            } else {
                return null;
            }
        }
        if (j != 0) {
            if (i2 + 2 > 16) {
                return null;
            }
            arrayOfByte1[(i2++)] = ((byte) (k >> 8 & 0xFF));
            arrayOfByte1[(i2++)] = ((byte) (k & 0xFF));
        }
        if (i != -1) {
            i4 = i2 - i;
            if (i2 == 16) {
                return null;
            }
            for (i1 = 1; i1 <= i4; i1++) {
                arrayOfByte1[(16 - i1)] = arrayOfByte1[(i + i4 - i1)];
                arrayOfByte1[(i + i4 - i1)] = 0;
            }
            i2 = 16;
        }
        if (i2 != 16) {
            return null;
        }
        byte[] arrayOfByte2 = convertFromIPv4MappedAddress(arrayOfByte1);
        if (arrayOfByte2 != null) {
            return arrayOfByte2;
        }
        return arrayOfByte1;
    }

    public static boolean isIPv4LiteralAddress(String paramString) {
        return textToNumericFormatV4(paramString) != null;
    }

    public static boolean isIPv6LiteralAddress(String paramString) {
        return textToNumericFormatV6(paramString) != null;
    }

    public static byte[] convertFromIPv4MappedAddress(byte[] paramArrayOfByte) {
        if (isIPv4MappedAddress(paramArrayOfByte)) {
            byte[] arrayOfByte = new byte[4];
            System.arraycopy(paramArrayOfByte, 12, arrayOfByte, 0, 4);
            return arrayOfByte;
        }
        return null;
    }

    private static boolean isIPv4MappedAddress(byte[] paramArrayOfByte) {
        if (paramArrayOfByte.length < 16) {
            return false;
        }
        if ((paramArrayOfByte[0] == 0) && (paramArrayOfByte[1] == 0) && (paramArrayOfByte[2] == 0) && (paramArrayOfByte[3] == 0)
                && (paramArrayOfByte[4] == 0) && (paramArrayOfByte[5] == 0) && (paramArrayOfByte[6] == 0) && (paramArrayOfByte[7] == 0)
                && (paramArrayOfByte[8] == 0) && (paramArrayOfByte[9] == 0) && (paramArrayOfByte[10] == -1) && (paramArrayOfByte[11] == -1)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        System.out.println(RequestInfoUtil.IP_LIST);
    }
}
